﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using Citi.GocAttributes.Domain;
using Citi.GocAttributes.WebUI.Models;
using Citi.GocAttributes.Domain.Utils;
using System.Threading;
//using Citi.GocAttributes.WebUI.Util;

namespace Citi.GocAttributes.WebUI.Controllers
{
    public class RuleController : ControllerBase<RuleModel, Rule>
    {
        delegate string ProcessTask(string id, DateTime referenceDate);

        readonly RuleApplication applyRule = new RuleApplication();

        public void StartApplyRules(string id, int year, int month)
        {
            var referenceDate = new DateTime(year, month, 1);
            //new RuleApplication().Apply(referenceDate);
            applyRule.Remove(id);
            applyRule.Add(id);
            var processTask = new ProcessTask(applyRule.ProcessLongRunningAction);
            processTask.BeginInvoke(id, referenceDate, EndLongRunningProcess, processTask);
        }

        public void EndLongRunningProcess(IAsyncResult result)
        {
            var processTask = (ProcessTask)result.AsyncState;
            var id = processTask.EndInvoke(result);
            applyRule.Remove(id);
        }

        public ContentResult GetCurrentProgress(string id)
        {
            ControllerContext.HttpContext.Response.AddHeader("cache-control", "no-cache");
            return Content(applyRule.GetStatus(id));
        }

        public ActionResult Index()
        {
            //IndexBase();
            //Models = Models.Where(x => !x.Exception).ToList();
            return View(GetActives());
        }

        public ActionResult Delete(int id)
        {
            DeleteBase(id);

            return PartialView("List", GetActives());
        }

        [HttpGet]
        //[OutputCache(NoStore = true, Duration = 1, VaryByParam = "*")]
        public ActionResult Form(int? id)
        {
            var result = ActionBase(id, true);

            ViewBag.Orders = new List<short> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }.ToSelectList(x => x.ToString(), x => x.ToString(), id.HasValue ? Domain.Order.ToString() : null);

            return result;
        }

        [OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
        public ActionResult Form(RuleModel model, FormCollection collection)
        {
            model.Exception = !string.IsNullOrEmpty(collection["GocsId"]);

            if (model.Exception && !string.IsNullOrEmpty(collection["ruleIdException"]))
            {
                SaveGocs(collection["GocsId"], int.Parse(collection["ruleIdException"]));
                return RedirectToAction("Exceptions", "Goc");
            }

            if (model.Exception)
                model.Name = "Exception";

            var domain = model.CreateDomain();

            if (model.Id == 0)
            {
                SaveNewNoChanges(domain, collection);

                while (domain.Id == 0) SaveNewNoChanges(domain, collection);
            }
            else
            {
                domain = new Rule().SelectById<Rule>(model.Id);

                if (!domain.GocRules.Any())
                    SaveNewNoChanges(domain, collection);
                else
                {
                    domain.Active = false;
                    domain.LastUpdate = DateTime.Now;
                    domain.Save(domain);

                    domain = model.CreateDomain(domain);
                    domain = domain.CopyEntity(domain);
                    SaveNewNoChanges(domain, collection);
                }
            }

            if (domain.Exception)
            {
                SaveGocs(collection["GocsId"], domain.Id);
                return RedirectToAction("Exceptions", "Goc");
            }

            return PartialView("List", GetActives());
        }

        private static void SaveGocs(string gocsIdCollection, int ruleId)
        {
            var gocsId = gocsIdCollection.Split(new[] { "|" }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse);
            var gocs = new Goc().SelectAllActive<Goc>().Where(x => gocsId.Contains(x.Id));

            foreach (var goc in gocs)
            {
                goc.GocRules.Add(new GocRule
                {
                    Goc = goc,
                    RuleId = ruleId,
                    Order = 0,
                    ReferenceDate = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1),
                });

                goc.Save(goc);
            }
        }

        private static IEnumerable<RuleModel> GetActives()
        {
            return new RuleModel().CreateModel(new Rule().GetActiveRulesButException());
        }

        private void SaveNewNoChanges(Rule domain, FormCollection collection)
        {
            domain.ReferenceDate = DateTime.Today;
            domain.LastUpdate = DateTime.Now;

            if (collection.AllKeys.Where(x => x == "Name").Any())
                domain.Name = collection["Name"];

            domain.Active = true;

            FillRuleConditions(domain, collection);
            FillRuleResults(domain, collection);

            domain.Save(domain);
        }

        private void FillRuleResults(Rule domain, FormCollection collection)
        {
            var keys = collection.AllKeys.Where(x => x.StartsWith("Value") && !string.IsNullOrEmpty(collection[x])).ToList();

            if (keys.Count == 0 && domain.Id != 0) return;

            var typeRuleResults = new TypeRuleResult().SelectAllActive<TypeRuleResult>();

            foreach (var key in keys)
            {
                var typeRuleResultId = int.Parse(collection[key]);
                //var ruleResult = domain.RuleResults.OrderByDescending(x=> x.Id).FirstOrDefault(x => x.TypeRuleResultId == typeRuleResultId);

                var ruleResult = domain.RuleResults.Where(x => x.TypeRuleResult != null && x.TypeRuleResult.Type == typeRuleResults.FirstOrDefault(y => y.Id == typeRuleResultId).Type);

                if (ruleResult.Count() > 1)
                    ruleResult.Where(x=> x.Id != ruleResult.First().Id).ToList().ForEach(x => x.Delete(x));

                if (!ruleResult.Any())
                    domain.RuleResults.Add(new RuleResult { TypeRuleResultId = typeRuleResultId });
                else
                    ruleResult.First().TypeRuleResultId = typeRuleResultId;
            }

            var types = keys.Select(x => (int)Enum.Parse(typeof(DatabaseEnums.TypeResultType), x.Replace("Value", string.Empty)));

            var resultToDelete = new List<RuleResult>();

            foreach (var ruleResult in domain.RuleResults.Where(x => x.TypeRuleResult != null && !types.Contains(x.TypeRuleResult.Type)))
                resultToDelete.Add(ruleResult);

            resultToDelete.ForEach(x => x.Delete(x));
        }

        private void FillRuleConditions(Rule domain, FormCollection collection)
        {
            var keys = collection.AllKeys.Where(x => x.StartsWith("ConditionValue")
                && !string.IsNullOrEmpty(collection[x])
                && x != "ConditionValueFunction" && x != "ConditionValueManagedSegment").ToList();

            if (keys.Count == 0 && domain.Id != 0) return;

            foreach (var key in keys)
            {
                var type = (DatabaseEnums.RuleType)Enum.Parse(typeof(DatabaseEnums.RuleType), key.Replace("ConditionValueId", string.Empty).Replace("ConditionValue", string.Empty));

                var ruleCondition = domain.RuleConditions.FirstOrDefault(x => x.Type == (int)type);

                if (ruleCondition == null)
                {
                    domain.RuleConditions.Add(new RuleCondition
                        {
                            LastUpdate = DateTime.Now,
                            ReferenceDate = DateTime.Today,
                            Type = (short)type,
                            Value =
                                type != DatabaseEnums.RuleType.Function && type != DatabaseEnums.RuleType.ManagedSegment
                                    ? collection[key]
                                    : null,
                            FunctionId =
                                type == DatabaseEnums.RuleType.Function
                                    ? int.Parse(collection["ConditionValueIdFunction"])
                                    : (int?)null,
                            ManagedSegmentId =
                                type == DatabaseEnums.RuleType.ManagedSegment
                                    ? int.Parse(collection["ConditionValueIdManagedSegment"])
                                    : (int?)null
                        });
                }
                else
                {
                    ruleCondition.LastUpdate = DateTime.Now;
                    ruleCondition.Value =
                        type != DatabaseEnums.RuleType.Function && type != DatabaseEnums.RuleType.ManagedSegment
                            ? collection[key]
                            : null;
                    ruleCondition.FunctionId =
                        type == DatabaseEnums.RuleType.Function
                            ? int.Parse(collection["ConditionValueIdFunction"])
                            : (int?)null;
                    ruleCondition.ManagedSegmentId =
                        type == DatabaseEnums.RuleType.ManagedSegment
                            ? int.Parse(collection["ConditionValueIdManagedSegment"])
                            : (int?)null;
                }
            }

            var types = keys.Select(x => (DatabaseEnums.RuleType)Enum.Parse(typeof(DatabaseEnums.RuleType),
                x.Replace("ConditionValueId", string.Empty).Replace("ConditionValue", string.Empty)));

            var conditionsToDelete = new List<RuleCondition>();

            foreach (var ruleCondition in domain.RuleConditions.Where(x => !types.Contains((DatabaseEnums.RuleType)x.Type)))
                conditionsToDelete.Add(ruleCondition);

            conditionsToDelete.ForEach(x => x.Delete(x));
        }

        [HttpGet]
        public JsonResult GetAutocomplete(string searchTerm)
        {
            var rules = new Rule().GetActiveRulesButException()
                            .Where(x => x.Name.ToUpper().Contains(searchTerm.ToUpper()))
                            .GroupBy(c => c.Name)
                            .Select(g => g.OrderByDescending(c => c.ReferenceDate).First())
                            .Select(c => new { c.Id, c.Name });

            return Json(rules.Select(x => new { x.Id, Description = x.Name }), JsonRequestBehavior.AllowGet);
        }
    }

    internal class RuleApplication : Domain.Utils.RuleApplication
    {
        public override void Status(string message)
        {
            ProcessStatus[Id] = message;
            Thread.Sleep(200);
        }
    }
}
